package top.haijunit.work.core.redis;

import cn.hutool.extra.spring.SpringUtil;
import lombok.Data;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.Assert;
import top.haijunit.work.core.doamin.ExceptionEnum;
import top.haijunit.work.core.doamin.ExceptionServer;

import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Redisson分布式锁服务
 *
 * @author zhanghaijun
 * @since 2024-07-31
 */
@Slf4j
@UtilityClass
public class RedissonLockUtils {

    private RedissonClient redissonClient;

    private RedissonClient getRedissonClient() {
        if (null == redissonClient) {
            redissonClient = SpringUtil.getBean(RedissonClient.class);
        }
        Assert.notNull(redissonClient, "RedissonClient 未注入，请检查配置 redis 连接！");
        return redissonClient;
    }

    /**
     * 加锁执行,默认时间配置
     *
     * @param lockKey  key
     * @param request  请求参数
     * @param consumer 业务处理
     * @param <T>      参数类型
     */
    public <T> void execute(String lockKey, T request, Consumer<T> consumer) {
        execute(RedissonLockWrapper.lockWithDefaultTime(lockKey), request, consumer);
    }

    /**
     * 加锁执行,默认时间配置
     *
     * @param lockKey  key
     * @param request  请求参数
     * @param function 业务处理
     * @param <T>      参数类型
     * @param <R>      应答类型
     * @return 处理结果
     */
    public <T, R> R execute(String lockKey, T request, Function<T, R> function) {
        return execute(RedissonLockWrapper.lockWithDefaultTime(lockKey), request, function);
    }

    /**
     * 加锁执行,默认时间配置
     *
     * @param lockKey  key
     * @param runnable 业务处理
     */
    public void execute(String lockKey, Runnable runnable) {
        execute(RedissonLockWrapper.lockWithDefaultTime(lockKey), runnable);
    }

    /**
     * 加锁执行,默认时间配置
     *
     * @param lockKey  key
     * @param callable 业务处理
     * @return 处理结果
     */
    public <T> T execute(String lockKey, Callable<T> callable) {
        return doExecute(RedissonLockWrapper.lockWithDefaultTime(lockKey), callable);
    }

    /**
     * 加锁执行,指定时间配置
     *
     * @param lockConfigBO 配置
     * @param request      请求参数
     * @param consumer     业务处理
     * @param <T>          参数类型
     */
    public <T> void execute(RedissonLockWrapper lockConfigBO, T request, Consumer<T> consumer) {
        doExecute(lockConfigBO, () -> {
            consumer.accept(request);
            return "SUCCESS";
        });
    }

    /**
     * 加锁执行,指定时间配置
     *
     * @param lockConfigBO 配置
     * @param request      请求参数
     * @param function     业务处理
     * @param <T>          参数类型
     * @param <R>          应答类型
     * @return 处理结果
     */
    public <T, R> R execute(RedissonLockWrapper lockConfigBO, T request, Function<T, R> function) {
        return doExecute(lockConfigBO, () -> function.apply(request));
    }

    /**
     * 加锁执行,指定时间配置
     *
     * @param lockConfigBO 配置
     * @param runnable     业务处理
     */
    public void execute(RedissonLockWrapper lockConfigBO, Runnable runnable) {
        doExecute(lockConfigBO, () -> {
            runnable.run();
            return "SUCCESS";
        });
    }

    /**
     * 加锁执行,指定时间配置
     *
     * @param lockConfigBO 配置
     * @param callable     业务处理
     */
    public <T> T execute(RedissonLockWrapper lockConfigBO, Callable<T> callable) {
        return doExecute(lockConfigBO, callable);
    }

    private <T> T doExecute(RedissonLockWrapper lockConfigBO, Callable<T> callable) {
        RLock lock = getRedissonClient().getLock(lockConfigBO.getLockKey());
        try {
            tryLock(lock, lockConfigBO.getWaitTime(), lockConfigBO.getLeaseTime());
            return callable.call();
        } catch (Exception e) {
            log.error("执行异常", e);
            throw new ExceptionServer(ExceptionEnum.GET_LOCK_ERROR, e);
        } finally {
            tryUnlock(lock);
        }
    }

    private void tryLock(RLock lock, long waitTime, long leaseTime) throws InterruptedException {
        if (Objects.equals(waitTime, -1L) || Objects.equals(leaseTime, -1L)) {
            lock.lock();
            return;
        }
        boolean lockSuccess = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
        if (!lockSuccess) {
            throw new ExceptionServer(ExceptionEnum.GET_LOCK_ERROR);
        }
    }

    private void tryUnlock(RLock lock) {
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

    @Data
    public static class RedissonLockWrapper {
        /**
         * 加锁key
         */
        private String lockKey;

        /**
         * 获取锁的等待时间(单位ms, 值为-1时阻塞直到加锁成功)
         */
        private Long waitTime;

        /**
         * 获取锁的执行时间(单位ms, 值为-1时阻塞直到加锁成功)
         */
        private Long leaseTime;

        public static RedissonLockWrapper lockWithDefaultTime(String lockKey) {
            RedissonLockWrapper lockConfigBO = new RedissonLockWrapper();
            lockConfigBO.setLockKey(lockKey);
            lockConfigBO.setWaitTime(3000L);
            lockConfigBO.setLeaseTime(5000L);
            return lockConfigBO;
        }

        public static RedissonLockWrapper lockWithUnlimitedTime(String lockKey) {
            RedissonLockWrapper lockConfigBO = new RedissonLockWrapper();
            lockConfigBO.setLockKey(lockKey);
            lockConfigBO.setWaitTime(-1L);
            lockConfigBO.setLeaseTime(-1L);
            return lockConfigBO;
        }
    }
}
